package com.gridpower.spreadsheet.client.widgets;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.TreeMap;
import java.util.Map.Entry;

import com.google.gwt.core.client.GWT;
import com.google.gwt.dom.client.Document;
import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.Element;
import com.google.gwt.user.client.Event;
import com.google.gwt.user.client.EventListener;
import com.google.gwt.user.client.Timer;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.ui.AbsolutePanel;
import com.google.gwt.user.client.ui.Composite;
import com.google.gwt.user.client.ui.FocusPanel;
import com.google.gwt.user.client.ui.Panel;
import com.google.gwt.user.client.ui.ScrollPanel;
import com.google.gwt.user.client.ui.UIObject;
import com.google.gwt.user.client.ui.Widget;
import com.google.gwt.widgetideas.graphics.client.Color;
import com.google.gwt.widgetideas.graphics.client.GWTCanvas;
import com.gridpower.spreadsheet.client.models.AppModel;
import com.gridpower.spreadsheet.client.models.DocModel;
import com.gridpower.spreadsheet.client.models.GridCell;
import com.gridpower.spreadsheet.client.models.UIDataRender;
import com.gridpower.spreadsheet.client.models.UIMouseModel;
import com.gridpower.spreadsheet.client.models.UISelectModel;
import com.gridpower.spreadsheet.client.models.AppModel.CellSelection;
import com.gridpower.spreadsheet.client.models.AppModel.TimerListener;
/**
 * It is a better choice not to render all the four part
 * in the parent view; because the scroll may cause complex
 * action
 * reconstruction needed, Rect not only used here
 * All the three layers should have the same size as posInAppView
 * no need to public from Absolute Panel, because no need to change the behavior
 * @author 438
 *
 */
public class GridPart {
	public static class Rect {
		public int _top = 0;
		public int _left = 0;
		public int _width = 0;
		public int _height = 0;
		public  Rect(int left, int top, int width, int height) {
			_top = top;
			_left = left;
			_width = width;
			_height = height;
		}
	}
	public static class Position {
		public int _x = 0;
		public int _y = 0;
	}
	public static class Size {
		public int _width = 0;
		public int _height = 0;
		public Size(int wid, int hgt) {
			_width = wid;
			_height = hgt;
		}
	}
	private Rect posInAppView;
	private int cellRangeStartRow = 0;
	private int cellRangeStartCol = 0;
	private int cellRangeEndRow = 0;
	private int cellRangeEndCol = 0;
	
	private int viewableRowStart = 0;
	private int viewableRowEnd = 0;
	private int viewableColStart = 0;
	private int viewableColEnd = 0;
	// content width and height
	private Size contentSize;
	// viewable offset
	private Position viewableTopLeftPos;
	// is it normal or  some "special" part
	private String id = "normal";
	
	// The panel and the canvas
	private AbsolutePanel heldPanel = new AbsolutePanel() {
		@Override
		public void onBrowserEvent(Event evt) {
			//System.out.println("widget"+DOM.eventGetType(evt));
			handleBrowserEvent(evt);
		}
	};
	class Canvas extends GWTCanvas{
		public Canvas() {
			this.sinkEvents( DOM.getEventsSunk(this.getElement())
					| Event.ONMOUSEDOWN);// it works

		}
		@Override// not event to this panel, if needed should pass in
		public void onBrowserEvent(Event evt) {
			switch (DOM.eventGetType(evt)) {
				case Event.FOCUSEVENTS: {
					//System.out.println("canvas"+DOM.eventGetType(evt));
					break;
				}
				case Event.ONMOUSEDOWN: {
					//System.out.println("canvas mouse down"+DOM.eventGetType(evt));
					break;
				}
			}
			
		}
	}
	private GWTCanvas gridCanvas = new GWTCanvas();
	private TextLayer txtCanvas = new TextLayer();// we don't need this,need! move whole together
	protected GWTCanvas decorateCanvas = new GWTCanvas();
	protected GWTCanvas decorateCanvas1 = new GWTCanvas();
	/**
	 * state recorder, deprecated
	 */
	int mouseStatus = 0;
	boolean downInside = false;
	int mouseDownCellRow = 0;
	int mouseDownCellCol = 0;
	
	//boolean upInside = false;
	int mouseUpCellRow = 0;
	int mouseUpCellCol = 0;
	// test
	int moveCnt;
	/**
	 * event listeners 
	 */

	/**
	 *  the constructor
	 * */
	public void setGridPartId(String id) {
		this.id = id;
		if (this.id.equals("botRight")) {
			heldPanel.sinkEvents( DOM.getEventsSunk(heldPanel.getElement())
					|Event.ONMOUSEWHEEL);
		}
	}
	public GridPart() {
		// register event listner
		// only one such listener may exsist
		//DOM.setEventListener(heldPanel.getElement(), this);
		/*
		 * Note that classes extending Widget do not pass browser events like mouse clicks to widgets inside it. So you need to override onBrowserEvent() and pass events there
		 */
		heldPanel.sinkEvents( DOM.getEventsSunk(heldPanel.getElement())
				|Event.MOUSEEVENTS | Event.ONCLICK);

		//gridCanvas.sinkEvents( DOM.getEventsSunk(gridCanvas.getElement())|Event.MOUSEEVENTS | Event.ONCLICK);
		//Composite
		//FocusPanel
		// DOM.setEventListener(heldPanel.getElement(), this);
		heldPanel.add(gridCanvas, 0, 0);
		heldPanel.add(txtCanvas, 0, 0);
		heldPanel.add(decorateCanvas, 0, 0);
		heldPanel.add(decorateCanvas1, 0, 0);
		
		//gridCanvas.closePath()
		
		DOM.setStyleAttribute(decorateCanvas.getElement(),
				 "opacity", "0.5");
		DOM.setStyleAttribute(decorateCanvas.getElement(),
				"filter",
				"Alpha(opacity=" + "50" + ")");
		decorateCanvas.setBackgroundColor(Color.BLACK);
		decorateCanvas.setLineWidth(2);
		decorateCanvas.setStrokeStyle(Color.GREEN);

		DOM.setStyleAttribute(decorateCanvas1.getElement(),
				 "opacity", "0.5");
		DOM.setStyleAttribute(decorateCanvas1.getElement(),
				"filter",
				"Alpha(opacity=" + "50" + ")");
		decorateCanvas1.setBackgroundColor(Color.BLACK);
		decorateCanvas1.setLineWidth(2);
		decorateCanvas1.setStrokeStyle(Color.GREEN);

		
		
		//decorateCanvas.sinkEvents(DOM.getEventsSunk(decorateCanvas.getElement())|Event.MOUSEEVENTS | Event.ONCLICK);
		//txtCanvas.sinkEvents(DOM.getEventsSunk(txtCanvas.getElement())|Event.MOUSEEVENTS | Event.ONCLICK);
		//gridCanvas.sinkEvents(DOM.getEventsSunk(gridCanvas.getElement())|Event.MOUSEEVENTS | Event.ONCLICK);
		/*
		 * add the data render listener to UIModel.
		 */
		// test code:

		
		AppModel.getInstance().getMouseModel().attachRender(new UIDataRender() {
			public void renderData() {
				//mouseUpCellCol = -1;
				// only set the data of decoration part
			}
		});
		
		AppModel.getInstance().getSelectModel().attachRender(new UIDataRender() {
			public void renderData() {// draw both current and next, so two decorator needed
				// draw selection inside my part
				ArrayList rowStarts = UIUtil.getAppView().getRowStarts();
				ArrayList colStarts = UIUtil.getAppView().getColStarts();
				if (rowStarts.size() == 0)
					return;
				UIMouseModel mouseModel = AppModel.getInstance().getMouseModel();
				UISelectModel selectModel = AppModel.getInstance().getSelectModel();
				CellSelection sel = selectModel.getCurrentSelect();
				int rowStart, rowEnd, colStart, colEnd;
				int y1 = 0,x1 = 0,y2 = 0,x2 = 0;
				
				int biasy = (Integer)rowStarts.get(cellRangeStartRow),
					biasx = (Integer)colStarts.get(cellRangeStartCol);

				boolean isNotInside = (sel.rowStart > cellRangeEndRow
						|| sel.colStart > cellRangeEndCol
						|| sel.rowEnd < cellRangeStartRow
						|| sel.colEnd < cellRangeStartCol);
				if (isNotInside) {
					decorateCanvas.clear();
					decorateCanvas.setVisible(false);
					
				} else {
					decorateCanvas.setVisible(true);
				
					//if (mouseModel.isMouseDown() && mouseModel.getObjectMouseDownOn() == UIMouseModel.OBJ_CELL) {
						rowStart = sel.rowStart < cellRangeStartRow ? cellRangeStartRow : sel.rowStart;
						rowEnd = sel.rowEnd > cellRangeEndRow ? cellRangeEndRow : sel.rowEnd;
						colStart = sel.colStart < cellRangeStartCol ? cellRangeStartCol : sel.colStart;
						colEnd = sel.colEnd > cellRangeEndCol ? cellRangeEndCol : sel.colEnd;
						y1 = (Integer)rowStarts.get(rowStart)-biasy; y2 = (Integer)rowStarts.get(rowEnd+1)-biasy;
						x1 = (Integer)colStarts.get(colStart)-biasx; x2 = (Integer)colStarts.get(colEnd+1) - biasx;
						// new optimization

						int width = (Math.abs(x2-x1+1)), height = (Math.abs(y2-y1+1));
						if (! (width == decorateCanvas.getCoordWidth()
								&& height == decorateCanvas.getCoordHeight()))//fast!:keep element of path?, set attribute fault?
							decorateCanvas.resize(width,height);
						heldPanel.setWidgetPosition(decorateCanvas, x1, y1);
						//decorateCanvas.setBackgroundColor(Color.BLACK);
						decorateCanvas.clear();//slow!! a must!not sure, only in GWT browser?
					//}
						/*
						decorateCanvas.beginPath();
						decorateCanvas.moveTo(0, 0);
						decorateCanvas.lineTo(0, y2-y1);
						decorateCanvas.lineTo(x2-x1, y2-y1);
						decorateCanvas.lineTo(x2-x1, 0);
						decorateCanvas.closePath();
						decorateCanvas.stroke();
						*/
					System.out.println("make a draw:" + x1 + "," + y1 + "," + x2 + "," + y2);
				}
				//decorateCanvas.setLineWidth(1);
				/*
				decorateCanvas.beginPath();
				decorateCanvas.moveTo(0, 0);
				decorateCanvas.lineTo(0, y2-y1);
				decorateCanvas.lineTo(x2-x1, y2-y1);
				decorateCanvas.lineTo(x2-x1, 0);
				decorateCanvas.closePath();
				decorateCanvas.stroke();
				*/
				
				//save context
				/*DOM.setStyleAttribute(decorateCanvas.getElement(),
						"filter",
						"Alpha(opacity=" + "50" + ")");*/
				if (mouseModel.isMouseDown()) {
					if (mouseModel.getObjectMouseDownOn()
							== UIMouseModel.OBJ_SELECTIONEDGE) {
						sel = selectModel.getNextSelect();
						// render next selection
						rowStart = sel.rowStart < cellRangeStartRow ? cellRangeStartRow : sel.rowStart;
						rowEnd = sel.rowEnd > cellRangeEndRow ? cellRangeEndRow : sel.rowEnd;
						colStart = sel.colStart < cellRangeStartCol ? cellRangeStartCol : sel.colStart;
						colEnd = sel.colEnd > cellRangeEndCol ? cellRangeEndCol : sel.colEnd;
						y1 = (Integer)rowStarts.get(rowStart)-biasy; y2 = (Integer)rowStarts.get(rowEnd+1)-biasy;
						x1 = (Integer)colStarts.get(colStart)-biasx; x2 = (Integer)colStarts.get(colEnd+1) - biasx;

						decorateCanvas1.setVisible(true);
						decorateCanvas1.resize((Math.abs(x2-x1+1)),
								(Math.abs(y2-y1+1)));
						heldPanel.setWidgetPosition(decorateCanvas1, x1, y1);
						/*
						decorateCanvas1.clear();
						decorateCanvas1.setStrokeStyle(Color.GREEN);
						decorateCanvas1.setLineWidth(3);

						decorateCanvas1.beginPath();
						decorateCanvas1.moveTo(0, 0);
						decorateCanvas1.lineTo(0, y2-y1);
						decorateCanvas1.lineTo(x2-x1, y2-y1);
						decorateCanvas1.lineTo(x2-x1, 0);
						decorateCanvas1.closePath();
						decorateCanvas1.stroke();
						*/
						// draw
					} else {
						decorateCanvas1.setVisible(false);
					}
				} else {
					// draw a small rect
					decorateCanvas1.setVisible(false);
					if (mouseModel.getObjectMouseMoveOn()
							== UIMouseModel.OBJ_SELECTIONEDGE) {// render the rectangle
						// draw a rectangle
						if (isNotInside == false) {
							/*rowStart = sel.rowStart < cellRangeStartRow ? cellRangeStartRow : sel.rowStart;
							rowEnd = sel.rowEnd > cellRangeEndRow ? cellRangeEndRow : sel.rowEnd;
							colStart = sel.colStart < cellRangeStartCol ? cellRangeStartCol : sel.colStart;
							colEnd = sel.colEnd > cellRangeEndCol ? cellRangeEndCol : sel.colEnd;
							y1 = (Integer)rowStarts.get(rowStart)-biasy; y2 = (Integer)rowStarts.get(rowEnd+1)-biasy;
							x1 = (Integer)colStarts.get(colStart)-biasx; x2 = (Integer)colStarts.get(colEnd+1) - biasx;
							*/
							// to do: draw only a line
							decorateCanvas.setStrokeStyle(Color.GREEN);
							decorateCanvas.setLineWidth(3);
							decorateCanvas.beginPath();
							decorateCanvas.moveTo(0, 0);
							decorateCanvas.lineTo(0, y2-y1);
							decorateCanvas.lineTo(x2-x1, y2-y1);
							decorateCanvas.lineTo(x2-x1, 0);
							decorateCanvas.closePath();
							decorateCanvas.stroke();
						}
					} else {// draw the rectangle
						
					}
				}
			}
		});
		
	}
	/**
	 * render this gridpart
	 */
	public void render() {
		// draw on the canvas and render the text
		// no matter how much we draw, we can set a large enought canvas
		gridCanvas.clear();
		// 1. render the grid canvas
		gridCanvas.setGlobalCompositeOperation(GWTCanvas.DESTINATION_OVER);
	    gridCanvas.setLineWidth(1);
	    gridCanvas.setBackgroundColor(Color.LIGHTGREY);
	    
	    //gridCanvas.setStrokeStyle(Color.GREEN);
		AppView appView = UIUtil.getAppView();
		ArrayList colStarts = appView.getColStarts();
		ArrayList rowStarts = appView.getRowStarts();
		// int hgt = contentSize._height
		
		gridCanvas.setStrokeStyle(Color.BLACK);
		//gridCanvas.beginPath();
		int startX = (Integer)colStarts.get(cellRangeStartCol);
		int startY = (Integer)rowStarts.get(cellRangeStartRow);
		gridCanvas.beginPath();
		int viewableYStart = (Integer)rowStarts.get(viewableRowStart) - startY;
		int viewableYEnd = (Integer)rowStarts.get(viewableRowEnd+1) - startY;
		int viewableXStart = (Integer)colStarts.get(viewableColStart) - startX;
		int viewableXEnd = (Integer)colStarts.get(viewableColEnd+1) - startX;
		
		for (int col = viewableColStart; col <= viewableColEnd+1; ++col) {
			int x = (Integer)colStarts.get(col) - startX;
			gridCanvas.moveTo(x, viewableYStart);
			//if (col>cellRangeStartCol)
				//gridCanvas.lineTo(x, Math.min(contentSize._height,100));//wcm:test
			//else
				gridCanvas.lineTo(x, viewableYEnd);
		}
		for (int row = viewableRowStart; row <= viewableRowEnd + 1; ++row) {
			int y = (Integer)rowStarts.get(row)-startY;
			gridCanvas.moveTo(viewableXStart, y);
			gridCanvas.lineTo(viewableXEnd, y);
		}
		
		gridCanvas.stroke();// for transform use
		// 2. render transparent text cells
		DocModel docModel = AppModel.getInstance().getCurrentDoc();
		TreeMap cells = docModel.getRowColMap();
		Iterator itr = cells.entrySet().iterator();
		while (itr.hasNext()) {
			Entry entry = ((Entry)itr.next());
			if ((Integer)entry.getKey() >= cellRangeStartRow
					&& (Integer)entry.getKey() <= cellRangeEndRow) {
				TreeMap tm = (TreeMap)(entry.getValue());

				Iterator itr1 = tm.entrySet().iterator();
				while (itr1.hasNext()) {
					entry = (Entry)itr1.next();
					if ((Integer)entry.getKey() >= cellRangeStartCol
							&& (Integer)entry.getKey() <= cellRangeEndCol) {
						GridCell cell = (GridCell)(entry.getValue());
						String content = cell.getDisplayString();
						int r = cell.getRow(), c = cell.getCol();
						renderCellText(r, c, 
								(Integer)colStarts.get(c)-startX, (Integer)rowStarts.get(r)-startY, cell);
					}
				}
			}
		}
	}
	/**
	 * render the content of row and col
	 * although we can deduce the x/y/wid/hgt from row/col, but this deducing work
	 * is not done in this function.
	 * @param row
	 * @param col
	 * @param x
	 * @param y
	 * @param wid
	 * @param hgt
	 */
	protected void  renderCellText(int row, int col, int x, int y, GridCell cell ) {
		Element div = UIUtil.getAppView().getCellTextElement(row, col);
		txtCanvas.drawTextInElement(div, x, y, cell.getWidth(), cell.getHeight(),
				cell.getDisplayString(),
				cell.getDisplayStyle().font,
				Integer.toString(cell.getDisplayStyle().fontWeight),
				Integer.toString(cell.getDisplayStyle().fontSize),
				cell.getDisplayStyle().color);
	}
	//////////////////
	// getters and setters
	////////////////
	public AbsolutePanel getHeldPanel() {
		return heldPanel;
	}
	public Rect getPosInAppView() {
		return posInAppView;
	}

	public Size getContentSize() {
		return contentSize;
	}
	public Position getViewableTopLeftPos() {
		return viewableTopLeftPos;
	}
	public int getCellRangeEndCol() {
		return cellRangeEndCol;
	}
	public int getCellRangeEndRow() {
		return cellRangeEndRow;
	}
	public int getCellRangeStartCol() {
		return cellRangeStartCol;
	}
	public int getCellRangeStartRow() {
		return cellRangeStartRow;
	}
	
	
	public void setCellRangeEndCol(int cellRangeEndCol) {
		this.cellRangeEndCol = cellRangeEndCol;
	}
	public void setCellRangeEndRow(int cellRangeEndRow) {
		this.cellRangeEndRow = cellRangeEndRow;
	}
	public void setCellRangeStartCol(int cellRangeStartCol) {
		this.cellRangeStartCol = cellRangeStartCol;
	}
	public void setCellRangeStartRow(int cellRangeStartRow) {
		this.cellRangeStartRow = cellRangeStartRow;
	}
	public void setHeldPanel(AbsolutePanel heldPanel) {
		this.heldPanel = heldPanel;
	}
	/**
	 * what is the viewable position in the parent view
	 * @param posInAppView
	 */
	public void setPosInAppView(Rect posInAppView, boolean isScrollable) {
		this.posInAppView = posInAppView;
		// set the held panel's height
		/*if (isScrollable == false) {
			heldPanel.setHeight(Integer.toString(posInAppView._height) + "px");
			heldPanel.setWidth(Integer.toString(posInAppView._width) + "px");
		} else*/ {
			heldPanel.setHeight(Integer.toString(contentSize._height) + "px");
			heldPanel.setWidth(Integer.toString(contentSize._width) + "px");
		}
	}
	/**
	 * what is the size the Panel actually is
	 * @param contentSize
	 */
	public void setContentSize(Size contentSize) {
		this.contentSize = contentSize;
		// reset the canvases size

		// must use resize?
		gridCanvas.resize(contentSize._width,
				contentSize._height);
		txtCanvas.setSize(Integer.toString(contentSize._width) + "px",
				Integer.toString(contentSize._height)+"px");
		//decorateCanvas.setSize(Integer.toString(contentSize._width),
				//Integer.toString(contentSize._height));
		//decorateCanvas.setBackgroundColor(Color.BLACK);
		//txtCanvas.setBackgroundColor(Color.ALPHA_RED);
	}
	public void setViewableTopLeftPos(Position viewableTopLeftPos) {
		this.viewableTopLeftPos = viewableTopLeftPos;
		// set the relative pos of the view? here or outside
	}
	///////////////
	// Event Listener
	/////////////
	/**
	 * Translate System Event into symatic Event// listener to the basic event
	 */
	//@Override
	public void handleBrowserEvent(Event event) {
		// ScrollPanel
		// FocusPanel, Focus
		//System.out.println(DOM.eventGetType(event));
		//GWT.log("event", null);
		UIMouseModel mouseModel = AppModel.getInstance().getMouseModel();
		UISelectModel selectModel = AppModel.getInstance().getSelectModel();
		DocModel docModel =AppModel.getInstance().getCurrentDoc();
		//DOM.eventCancelBubble(event, true);
		
		 switch (DOM.eventGetType(event)) {
	     // case Event.ONCLICK:
	    	//  break;
		 case Event.ONMOUSEWHEEL:
		 {
			 event.cancelBubble(true);
			 event.preventDefault();
			 int velo = event.getMouseWheelVelocityY();
			 
			 
			 ScrollPanel scroll = UIUtil.getAppView().getVirtualVertScroller();
			 int position = scroll.getScrollPosition();
			 int height = DOM.getElementPropertyInt(scroll.getElement(), "scrollHeight");
			 
			 position += velo * 32;
			 if (position > height)
				 position = height;
			 if (position < 0)
				 position = 0;
			 scroll.setScrollPosition(position);
			 scroll.onBrowserEvent(null);//null for update
			 
			 break;
		 }
	      case Event.ONMOUSEDOWN:
	      {
	    	  // get client point, not in scroll panel, copy from ??
	    	  AppView appView = UIUtil.getAppView();
	    	  Element senderElem = heldPanel.getElement();
	    	  int x = DOM.eventGetClientX(event)
	    	  	- DOM.getAbsoluteLeft(senderElem)
	    	  	//+ DOM.getElementPropertyInt(senderElem, "scrollLeft")
	    	  	+ Window.getScrollLeft();
	    	  int y = DOM.eventGetClientY(event)
	    	  	- DOM.getAbsoluteTop(senderElem)
	    	  	//+ DOM.getElementPropertyInt(senderElem, "scrollTop")
	    	  	+ Window.getScrollTop();
	    	  int row = -1, col = -1;
	    	  int[] cell = getCell(x, y);
	    	  row = cell[0];
	    	  col = cell[1];
	    	  // System.out.println(Integer.toString(x)+","+y);
	    	  if (row != -1 && col != -1) {
	    		  // if on edge
	    		  boolean isOnEdge = isOnEdge(row, col, x, y);
	    		  mouseModel.setMouseDown(true);
	    		  mouseModel.setDownX(x);
	    		  mouseModel.setDownY(y);
	    		  mouseModel.setDownRow(row);
	    		  mouseModel.setDownCol(col);
	    		  selectModel.setRowColSelection(false);
	    		  
	    		  if (isOnEdge == false) {
		    		  CellSelection sel = selectModel.getCurrentSelect();
		    		  sel.rowStart = row;
		    		  sel.rowEnd = row;
		    		  sel.colStart = col;
		    		  sel.colEnd = col;
		    		  mouseModel.setObjectMouseDownOn(UIMouseModel.OBJ_CELL);
	    		  } else {
	    			  mouseModel.setObjectMouseDownOn(UIMouseModel.OBJ_SELECTIONEDGE);
	    			  //System.out.println("Down on Edge!");
	    			  CellSelection sel = selectModel.getCurrentSelect();
	    			  CellSelection selNext = selectModel.getNextSelect();
	    			  selNext.rowStart = sel.rowStart;
	    			  selNext.rowEnd = sel.rowEnd;
	    			  selNext.colStart = sel.colStart;
	    			  selNext.colEnd = sel.colEnd;
	    		  }
	    		  //mouseDownCellRow = row;
	    		  //mouseDownCellCol = col;
	    		  //downInside = true;
	    	  }
	    	  break;
	      }
	      case Event.ONMOUSEUP:
	      {
	    	  // get client point, not in scroll panel
	    	  Element senderElem = heldPanel.getElement();
	    	  int x = DOM.eventGetClientX(event)
	    	  	- DOM.getAbsoluteLeft(senderElem)
	    	  	//+ DOM.getElementPropertyInt(senderElem, "scrollLeft")
	    	  	+ Window.getScrollLeft();
	    	  int y = DOM.eventGetClientY(event)
	    	  	- DOM.getAbsoluteTop(senderElem)
	    	  	//+ DOM.getElementPropertyInt(senderElem, "scrollTop")
	    	  	+ Window.getScrollTop();
	    	  
	    	  int row = -1, col = -1;
	    	  int[] cell = getCell(x, y);
	    	  row = cell[0];
	    	  col = cell[1];
			  CellSelection sel = selectModel.getCurrentSelect();
			  CellSelection nextSel = selectModel.getNextSelect();
	    	  if (row != -1 && col != -1) {
	    		  if (mouseModel.isMouseDown() == true) {
	    			  moveCnt = 0;
	    			  if (mouseModel.getObjectMouseDownOn() == UIMouseModel.OBJ_SELECTIONEDGE) {
	    				  int delRow = row - mouseModel.getDownRow();
	    				  int delCol = col - mouseModel.getDownCol();

	    				  int rowCnt = docModel.getRowCount();
	    				  int colCnt = docModel.getColCount();
	    				  if (sel.rowStart+delRow >=0 && sel.rowStart+delRow < rowCnt
	    						  && sel.rowEnd+delRow >=0 && sel.rowEnd+delRow < rowCnt
	    						  && sel.colStart+delCol >=0 && sel.colStart+delCol < colCnt
	    						  && sel.colEnd+delCol >=0 && sel.colEnd+delCol < colCnt) {
	    					  nextSel.rowStart = sel.rowStart + delRow;// sel not next sel
	    					  nextSel.rowEnd = sel.rowEnd + delRow;
	    					  nextSel.colStart = sel.colStart + delCol;
	    					  nextSel.colEnd = sel.colEnd + delCol;
	    				  }
	    			  } else if (mouseModel.getObjectMouseDownOn() == UIMouseModel.OBJ_CELL){
	    				  // continue select
	    				  //CellSelection sel = selectModel.getCurrentSelect();;
	    				  sel.rowStart = mouseModel.getDownRow();
	    				  sel.rowEnd = row;
	    				  sel.colEnd = col;
	    				  sel.colStart = mouseModel.getDownCol();
	    				  sel.normalize();
	    				  //System.out.println(Integer.toString(sel.rowStart)+","+sel.colStart+","+sel.rowEnd+","+sel.colEnd);
	    			  } else if (mouseModel.getObjectMouseDownOn()
	    					  == UIMouseModel.OBJ_HORIRULER) {
	    				  sel.colStart = mouseModel.getDownCol();
	    				  sel.colEnd = col;
	    				  sel.normalize();
	    				 // selectModel.fireModelChange();
	    			  }else if (mouseModel.getObjectMouseDownOn()
	    					  == UIMouseModel.OBJ_VERTRULER ) {
	    				  sel.rowStart = mouseModel.getDownRow();
	    				  sel.rowEnd = row;
	    				  sel.normalize();
	    				  selectModel.fireModelChange();			  
	    			  }
	    			  selectModel.fireModelChange();
	    		  }
	    		  // transfer the data from model to remote server
	    	  }
	    	  if (mouseModel.isMouseDown() == true
	    			  && mouseModel.getObjectMouseDownOn() == UIMouseModel.OBJ_SELECTIONEDGE) {
	    		  sel.rowStart = nextSel.rowStart;
	    		  sel.rowEnd = nextSel.rowEnd;
	    		  sel.colStart = nextSel.colStart;
	    		  sel.colEnd = nextSel.colEnd;
	    	  }
	    	  mouseModel.setMouseDown(false);//release the button
	    	  break;
	      }
	      case Event.ONMOUSEMOVE:
	      {
	    	  //System.out.println("move");
	    	  
	    	  //System.out.println("move");
	    	  // get client point, not in scroll panel
	    	  Element senderElem = heldPanel.getElement();
	    	  int x = DOM.eventGetClientX(event)
	    	  	- DOM.getAbsoluteLeft(senderElem)
	    	  	//+ DOM.getElementPropertyInt(senderElem, "scrollLeft")
	    	  	+ Window.getScrollLeft();
	    	  int y = DOM.eventGetClientY(event)
	    	  	- DOM.getAbsoluteTop(senderElem)
	    	  	//+ DOM.getElementPropertyInt(senderElem, "scrollTop")
	    	  	+ Window.getScrollTop();
	    	  
	    	  int row = -1, col = -1;
	    	  int[] cell = getCell(x, y);
	    	  row = cell[0];
	    	  col = cell[1];
	    	  //System.out.println("Move on:" + row + "," + col);

	    	  if (row != -1 && col != -1) {
	    		  if (mouseModel.isMouseDown() == true) {
	    			  moveCnt++;

	    			  System.out.println("Down And Move:" + moveCnt);

	    			  
	    			  if (mouseModel.getObjectMouseDownOn() == UIMouseModel.OBJ_SELECTIONEDGE) {
	    				  int delRow = row - mouseModel.getDownRow();
	    				  int delCol = col - mouseModel.getDownCol();
	    				  CellSelection sel = selectModel.getCurrentSelect();
	    				  CellSelection nextSel = selectModel.getNextSelect();
	    				  int rowCnt = docModel.getRowCount();
	    				  int colCnt = docModel.getColCount();

	    				  if (sel.rowStart + delRow < 0)
	    					  delRow = 0-sel.rowStart;
	    				  if (sel.rowEnd + delRow >= rowCnt)
	    					  delRow = rowCnt-1-sel.rowEnd;
	    				  if (sel.colStart + delCol < 0)
	    					  delCol = 0-sel.colStart;
	    				  if (sel.colEnd + delCol >= colCnt)
	    					  delCol = colCnt-1-sel.colEnd;
	    				  
	    				  {
	    					  int rs,re,cs,ce;
	    					  rs = sel.rowStart + delRow;
	    					  re = sel.rowEnd + delRow;
	    					  cs = sel.colStart + delCol;
	    					  ce = sel.colEnd + delCol;// answer the mouse up event
	    					  if (!(rs==nextSel.rowStart && re == nextSel.rowEnd
	    							  && cs==nextSel.colStart && ce == nextSel.colEnd)) {
	    						  nextSel.rowStart = rs;
	    						  nextSel.rowEnd = re;
	    						  nextSel.colStart = cs;
	    						  nextSel.colEnd = ce;
	    						  selectModel.fireModelChange();
	    					  }
	    				  }
	    			  } else if (mouseModel.getObjectMouseDownOn() == UIMouseModel.OBJ_CELL){
	    				  // continue select
	    				  // accelerate the speed
	    				  CellSelection sel = selectModel.getCurrentSelect();
	    				  sel.rowStart = mouseModel.getDownRow();
	    				  sel.rowEnd = row;
	    				  sel.colEnd = col;
	    				  sel.colStart = mouseModel.getDownCol();
	    				  sel.normalize();
    					  selectModel.fireModelChange();
	    				  //System.out.println(Integer.toString(sel.rowStart)+","+sel.colStart+","+sel.rowEnd+","+sel.colEnd);
	    			  } else if (mouseModel.getObjectMouseDownOn()
	    					  == UIMouseModel.OBJ_HORIRULER ) {
	    				  CellSelection sel = selectModel.getCurrentSelect();
	    				  sel.colStart = mouseModel.getDownCol();
	    				  sel.colEnd = col;
	    				  sel.normalize();
	    				  selectModel.fireModelChange();
	    			  } else if (mouseModel.getObjectMouseDownOn()
	    					  == UIMouseModel.OBJ_VERTRULER ) {
	    				  CellSelection sel = selectModel.getCurrentSelect();
	    				  sel.rowStart = mouseModel.getDownRow();
	    				  sel.rowEnd = row;
	    				  sel.normalize();
	    				  selectModel.fireModelChange();			  
	    			  }
	    			  
	    			  
	    			  
	    		  } else { // only over a cell
	    			  boolean isOnEdge = isOnEdge(row, col, x, y);
	    			  mouseModel.setOverRow(row);
	    			  mouseModel.setOverCol(col);
	    			  if (isOnEdge == true) {
	    				  mouseModel.setObjectMouseMoveOn(UIMouseModel.OBJ_SELECTIONEDGE);
	    				  //System.out.println("MouseMove on edge");
	    			  } else {
	    				  mouseModel.setObjectMouseMoveOn(UIMouseModel.OBJ_CELL);
	    			  }
	    			  selectModel.fireModelChange();
	    		  }
	    		  
	    	  }
	    	  break;
	      }
	      //case Event.on
	      case Event.FOCUSEVENTS: {
	    	  System.out.println("focus!");
	      }
	      case Event.ONMOUSEOVER:
	      case Event.ONMOUSEOUT:
	    	  break;
		 }
	}
	/////////////////
	// Helper functions
	// todo: reconstruct
	///////////////
	public static int binarySearch(ArrayList sortedVals, int st, int ed, int val, int bias) {
		int ret = (st + ed)/2;
		while (true) {
			if (val < (Integer)sortedVals.get(ret) - bias) {
				ed = ret;
			} else {
				st = ret;
			}
			ret = (st + ed)/2;
			if (st + 1 == ed) {
			  break;
			}
		}
		return ret;
	}
	/**
	 * @param x x coordinate in a not-scroll mode
	 * @param y y coordinate in a not-scroll mode
	 */
	int[] getCell(int x, int y) {
		int[] ret = new int[2];

		//GridCell ret = null;
		// calculate the position
		AppView appView = UIUtil.getAppView();
		ArrayList rowStarts = appView.getRowStarts();
		ArrayList colStarts = appView.getColStarts();
		int r = (Integer)rowStarts.get(cellRangeStartRow);
		int c = (Integer)colStarts.get(cellRangeStartCol);
		int locRow = -1, locCol = -1;
		if (y >= 0&& y <= (Integer)rowStarts.get(cellRangeEndRow+1)-r
				&& x >= 0&& x <= (Integer)colStarts.get(cellRangeEndCol+1)-c)
		{
			int st = cellRangeStartRow, ed = cellRangeEndRow+1;
			locRow = binarySearch(rowStarts, st, ed, y, r);
			st = cellRangeStartCol;
			ed = cellRangeEndCol+1;
			locCol = binarySearch(colStarts, st, ed, x, c);

		}
		ret[0] = locRow;
		ret[1] = locCol;
		return ret;
		/*
		DocModel docModel = AppModel.getInstance().getCurrentDoc();
		if (docModel != null) {
			TreeMap tm = docModel.getRowColMap();
			ret = ((TreeMap)tm.get(locRow)).get(locCol);
		}
		*/
		//return ret;
	}
	boolean isOnEdge(int row, int col, int x, int y) {
		AppView appView = UIUtil.getAppView();
		boolean isOnEdge = false;
		ArrayList rowStarts = appView.getRowStarts();
		ArrayList colStarts = appView.getColStarts();
		int r = (Integer)rowStarts.get(cellRangeStartRow);
		int c = (Integer)colStarts.get(cellRangeStartCol);
		CellSelection curSel = AppModel.getInstance().getSelectModel().getCurrentSelect();
		//UIMouseModel mouseModel = AppModel.getInstance().getMouseModel();
		//mouseModel.setMouseDown(true);
		int delta = 7;
		if (row >= curSel.rowStart && row <= curSel.rowEnd
				&& col >= curSel.colStart && col <= curSel.colEnd) {
			if (curSel.rowStart >= this.cellRangeStartRow) {
				int rowy = (Integer)rowStarts.get(curSel.rowStart);
				if (rowy + delta - r > y && rowy - r < y) {
					isOnEdge = true;
				}
			}
			if (isOnEdge == false && curSel.rowEnd <= this.cellRangeEndRow) {
				int rowy = (Integer)rowStarts.get(curSel.rowEnd+1);
				if (y > rowy-r-delta && y < rowy -r)
					isOnEdge = true;
			}
			if (isOnEdge == false && curSel.colStart >= this.cellRangeStartCol) {
				int colx = (Integer)colStarts.get(curSel.colStart);
				if (x > colx-c && x < colx-c+delta)
					isOnEdge = true;
			}
			if (isOnEdge == false && curSel.colEnd <= this.cellRangeEndCol) {
				int colx = (Integer)colStarts.get(curSel.colEnd+1);
				if (x > colx-c-delta && x < colx-c)
					isOnEdge = true;		  
			}
		}
		return isOnEdge;
	}
	public int getViewableColEnd() {
		return viewableColEnd;
	}
	public int getViewableColStart() {
		return viewableColStart;
	}
	public int getViewableRowEnd() {
		return viewableRowEnd;
	}
	public int getViewableRowStart() {
		return viewableRowStart;
	}
	public void setViewableColEnd(int viewableColEnd) {
		this.viewableColEnd = viewableColEnd;
	}
	public void setViewableColStart(int viewableColStart) {
		this.viewableColStart = viewableColStart;
	}
	public void setViewableRowEnd(int viewableRowEnd) {
		this.viewableRowEnd = viewableRowEnd;
	}
	public void setViewableRowStart(int viewableRowStart) {
		this.viewableRowStart = viewableRowStart;
	}

	/** 
	 * Enables or disables text selection for the element. A circular reference 
	 * will be created when disabling text selection. Disabling should be cleared 
	 * when the element is detached. See the <code>Component</code> source for 
	 * an example. 
	 * 
	 * @param elem the element 
	 * @param disable <code>true</code> to disable 
	 */ 

}
